use core::{convert::Infallible, net::Ipv4Addr};

use cmsis_rtos2::{
    error::Error,
    os_delay, os_kernel_get_tick_count,
    static_rtos::event_flags::{EventFlags, EventFlagsMem},
    OS_WAIT_FOREVER,
};
use embedded_hal::{
    blocking::spi::{Transfer, Write},
    digital::v2::OutputPin,
};
use periph_utils::GpioConfig;
use w5500_hl::{
    ll::{
        eh0::vdm::W5500, Interrupt, LinkStatus, Mode, Registers, Sn, SocketInterruptMask,
        SocketStatus,
    },
    net::Eui48Addr,
    Common, Tcp,
};

#[derive(Debug, GpioConfig)]
#[gpio(
    init = "gpio_init",
    io(name = "reset", io = "PA2", mode = "OUT_PP", set, reset),
    io(name = "int", io = "PA3", mode = "IN_FLOAT", read),
    io(name = "cs", io = "PA4", mode = "OUT_PP", set, reset,),
    io(name = "sck", io = "PA5", mode = "AF_PP"),
    io(name = "miso", io = "PA6", mode = "IN_FLOAT"),
    io(name = "mosi", io = "PA7", mode = "AF_PP")
)]
struct SpiConfig;

static EVENT_FLAGS_MEM: EventFlagsMem = EventFlagsMem::const_new();
static EVENT_FLAGS: UnsafeMut<EventFlags> = UnsafeMut::const_new();
const FLAG_INT: u32 = 0x01;

pub fn init() {
    let ef = EventFlags::new("w5500", &EVENT_FLAGS_MEM);
    unsafe {
        EVENT_FLAGS.init(ef);
    }

    SpiConfig::gpio_init();
    SpiConfig::cs_set();
    SpiConfig::reset_reset();
    os_delay(50);
    SpiConfig::reset_set();
    os_delay(200);

    let rcc: stm32f1::stm32f103::RCC = unsafe { core::mem::transmute(()) };
    rcc.apb2enr
        .modify(|_, w| w.spi1en().enabled().afioen().enabled());

    let spi: stm32f1::stm32f103::SPI1 = unsafe { core::mem::transmute(()) };
    spi.cr1.reset();
    spi.cr2.reset();
    spi.i2scfgr.reset();
    spi.cr1.write(|w| {
        w.ssm()
            .enabled()
            .ssi()
            .slave_not_selected()
            .spe()
            .enabled()
            .br()
            .div2()
            .mstr()
            .master()
            .cpol().idle_low()
            .cpha().first_edge()
            .dff().eight_bit()
    });

    unsafe {
        let afio: stm32f1::stm32f103::AFIO = core::mem::transmute(());
        afio.exticr1.modify(|_, w| w.exti3().variant(0));

        let exti: stm32f1::stm32f103::EXTI = core::mem::transmute(());
        exti.imr.modify(|_, w| w.mr3().unmasked());

        exti.ftsr.modify(|_, w| w.tr3().enabled());

        stm32f1::stm32f103::NVIC::unmask(stm32f1::stm32f103::Interrupt::EXTI3);
    }

    let mut w5500 = W5500::new(MySpi, MyCs);

    w5500.set_mr(Mode::default().rst()).unwrap();
    os_delay(10);

    let version = w5500.version().unwrap();
    defmt::println!("version:{}", version);

    w5500
        .set_shar(&Eui48Addr::new(0x30, 0x34, 0x21, 0x21, 0x11, 0x33))
        .unwrap();
    w5500.set_sipr(&Ipv4Addr::new(192, 168, 1, 100)).unwrap();
    w5500.set_subr(&Ipv4Addr::new(255, 255, 255, 0)).unwrap();
    w5500.set_gar(&Ipv4Addr::new(192, 168, 1, 100)).unwrap();
    w5500.set_rtr(1000).unwrap();
    w5500.set_rcr(3).unwrap();

    w5500
        .set_imr(Interrupt::default().set_conflict().set_unreach())
        .unwrap();
    w5500.set_simr(0x01).unwrap();
    w5500
        .set_sn_imr(
            Sn::Sn0,
            SocketInterruptMask::ALL_MASKED
                .unmask_con()
                .unmask_discon()
                .unmask_recv()
                .unmask_timeout(),
        )
        .unwrap();
    w5500.set_intlevel(1000).unwrap();

    w5500.tcp_listen(Sn::Sn0, 8080).unwrap();

    defmt::println!("start listen");

    loop {
        let ret = EVENT_FLAGS.wait_any_flags(FLAG_INT, 500);

        match ret {
            Err(Error::Timeout) => {
                if w5500.phycfgr().unwrap().lnk() == LinkStatus::Down {
                    defmt::println!("net closed");
                    if w5500.sn_sr(Sn::Sn0).unwrap().unwrap() != SocketStatus::Listen {
                        defmt::println!("re listen");
                        w5500.tcp_listen(Sn::Sn0, 8080).unwrap();
                    }
                }
            }
            _ => {}
        }

        let sir = w5500.sir().unwrap();
        if sir == 0 {
            continue;
        }

        let sn_ir = w5500.sn_ir(Sn::Sn0).unwrap();
        defmt::println!("sn_ir:{}", sn_ir);
        w5500.set_sn_ir(Sn::Sn0, sn_ir).unwrap();

        let sn_sr = w5500.sn_sr(Sn::Sn0).unwrap().unwrap();
        match sn_sr {
            SocketStatus::CloseWait => {
                w5500.tcp_listen(Sn::Sn0, 8080).unwrap();
            }
            SocketStatus::Established => {
                let mut buf = [0u8; 20];
                let n = w5500.tcp_read(Sn::Sn0, &mut buf).unwrap();
                if n != 0 {
                    defmt::println!("recv {} bytes", n);
                    w5500.tcp_write(Sn::Sn0, &buf[..n as usize]).unwrap();
                }
            }

            _ => {}
        }
    }
}

use stm32f1::stm32f103::interrupt;

use crate::utils::UnsafeMut;

#[interrupt]
unsafe fn EXTI3() {
    let exti: stm32f1::stm32f103::EXTI = core::mem::transmute(());
    if exti.pr.read().pr3().is_pending() {
        exti.pr.write(|w| w.pr3().clear());
        EVENT_FLAGS.set_flags(FLAG_INT);
    }
}

fn spi_rw(v: u8) -> u8 {
    let spi: stm32f1::stm32f103::SPI1 = unsafe { core::mem::transmute(()) };
    while spi.sr.read().txe().is_not_empty() {}
    spi.dr.write(|w| w.dr().bits(v as u16));
    while spi.sr.read().rxne().is_empty() {}
    let n = spi.dr.read().dr().bits() as u8;
    n
}

struct MySpi;

impl Transfer<u8> for MySpi {
    type Error = Infallible;

    fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
        for byte in words.iter_mut() {
            let n = spi_rw(*byte);
            *byte = n;
        }
        Ok(words)
    }
}

impl Write<u8> for MySpi {
    type Error = Infallible;
    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
        for byte in words.iter() {
            spi_rw(*byte);
        }
        Ok(())
    }
}

struct MyCs;

impl OutputPin for MyCs {
    type Error = Infallible;
    fn set_high(&mut self) -> Result<(), Self::Error> {
        SpiConfig::cs_set();
        Ok(())
    }
    fn set_low(&mut self) -> Result<(), Self::Error> {
        SpiConfig::cs_reset();
        Ok(())
    }
}
